/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hyce;

/**
 * This demonstrates password-based encryption (PBE) using the
 * DES algorithm.  As mentioned before, DES is not very secure,
 * but the SDK version used for this example does not support
 * other (stronger) PBE algorithms
 *
 * The text file "clear.txt" will be read, encrypted and written back
 * as "clear.txt.des".
 *
 * I've done all the "hardcoding" of the file names and password for
 * simplicity of discussion.
 *
 */

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public class FileEncryptor
{

   /**
    * Note: As with the pervious example, all kinds of exceptions
    * can be thrown in main.  See the API documentation for each
    * method used.
    */

    Password password = new Password();
    Encryptor encryptor = new Encryptor();
    Output output = new Output();

    /*
     * This was Team Encryption's original code.  It's been proven to
     * work with the decrypt method.  We have depricated this code because
     * it was too simple.  The professor requires a more complicated code to
     * make a larger example of the UML.  Only use this if something isn't
     * working with our other code.
     */
   public static void encrypt(String file, String pass) throws Exception
   {

      // File to encrypt.  It does not have to be a text file!
      try
      {
        String filename = file;

        // Password must be at least 8 characters (bytes) long

        String password = pass;
        FileInputStream inFile = new FileInputStream(filename);


        FileOutputStream outFile = new FileOutputStream(filename + ".des");

        // Use PBEKeySpec to create a key based on a password.
        // The password is passed as a character array

        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        SecretKeyFactory keyFactory =
            SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey passwordKey = keyFactory.generateSecret(keySpec);

        // PBE = hashing + symmetric encryption.  A 64 bit random
        // number (the salt) is added to the password and hashed
        // using a Message Digest Algorithm (MD5 in this example.).
        // The number of times the password is hashed is determined
        // by the interation count.  Adding a random number and
        // hashing multiple times enlarges the key space.

        byte[] salt = new byte[8];
        Random rnd = new Random();
        rnd.nextBytes(salt);
        int iterations = 100;

        //Create the parameter spec for this salt and interation count

        PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, iterations);

        // Create the cipher and initialize it for encryption.

        Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
        cipher.init(Cipher.ENCRYPT_MODE, passwordKey, parameterSpec);

        // Need to write the salt to the (encrypted) file.  The
        // salt is needed when reconstructing the key for decryption.

        outFile.write(salt);

        // Read the file and encrypt its bytes.

        byte[] input = new byte[64];
        int bytesRead;
        while ((bytesRead = inFile.read(input)) != -1)
        {
            byte[] output = cipher.update(input, 0, bytesRead);
            if (output != null) outFile.write(output);
        }

        byte[] output = cipher.doFinal();
        if (output != null) outFile.write(output);

        inFile.close();
        outFile.flush();
        outFile.close();
      }
      catch(FileNotFoundException ex)
      {
          System.out.println("Error: Couldn't find file: " + file);
      }
      catch(Exception ex)
      {
          System.out.println("Error occured while encrypting: " + ex.getMessage());
      }

  }

   public ArrayList toArrayList(FileInputStream in) throws Exception
   {
       ArrayList<Byte> list = new ArrayList<Byte>();
       int bytesRead;
       byte[] input = new byte[64];
       while ((bytesRead = in.read(input)) != -1)
       {
           for (byte b:input)
           {
               list.add((Byte) b);
           }
       }

       return list;
   }
   /*
    * Original, more complicated method of doing things.
    *
    * TESTED AND WORKING
    *
    * However, this function is based on the assumption that Team Encryption
    * is taking in a FileInputStream.  If that is the case, then use this, otherwise
    * use the encrypt(object, string, string) below.
    */
   public void encryptWithFileStream(FileInputStream o,String nameOfFile,
           String password) throws Exception
   {
       this.output.setFileName(nameOfFile);
       this.output.createWriter();
       this.encryptor.initializeEncryption(password);
       this.encryptor.encrypt(toArrayList(o), this.output.getWriter());
   }

   /*
    * This method requires the object you want to encrypt, the name you want
    * to give it, and and password for the file.
    *
    * DO NOT TRY TO USE OUR DECRYPT AS IT DOES NOT WORK YET
    *
    * However the encryption method works.  You won't be able to open whatever
    * you encrypt, I promise :)
    *
    * I have not tested this method because I have not put any items into memory
    * that I could encrypt, however it's a direct copy from encryptWithFileStream(),
    * that does actually work, so I'm not worried.
    */
   public void encrypt(Object o,String nameOfFile, String password) throws Exception
   {
       this.output.setFileName(nameOfFile);
       this.output.createWriter();
       this.encryptor.initializeEncryption(password);
       this.encryptor.encrypt(o, this.output.getWriter());
   }
}

